package com.hrms.domain;


import java.lang.reflect.Field;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.Transaction;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Link;
import com.google.appengine.api.datastore.Rating;
import com.google.appengine.api.datastore.Text;
import com.hrms.core.Constants;
import com.hrms.core.PMF;
import com.hrms.core.Utils;
import com.thoughtworks.xstream.XStream;

public abstract class BusinessObjectImpl implements BusinessObject{
	protected static final Logger log = Logger.getLogger(BusinessObjectImpl.class.getName());
	
	private Map<String, String[]> parameterMap;
	
	public String toXml() {
		XStream xs = new XStream();			
		xs.alias( getClass().getSimpleName(), getClass());		
		xs.omitField(BusinessObjectImpl.class, "parameterMap");
		return xs.toXML(this);
	}
	
	@Deprecated
	public static <T> T fromXml(Class T, String xml ) {
		XStream xs = new XStream();			
		xs.alias( T.getSimpleName(), T);				
		return (T) xs.fromXML(xml);
	}
	
		
	public void jdoPreStore() {
			//	no-op		
	}

	public BusinessObjectImpl(){}

	
	
	public Map<String, String[]> getParameterMap() {
		return parameterMap;
	}

	public void setParameterMap(Map<String, String[]> parameterMap) {
		this.parameterMap = parameterMap;
	}

	public BusinessObjectImpl(Map<String, String[]> parameterMap) throws IllegalArgumentException, IllegalAccessException, ParseException {
		this(parameterMap, StringUtils.EMPTY);
	}
	
	public BusinessObjectImpl(Map<String, String[]> parameterMap, String prefix) throws IllegalArgumentException, IllegalAccessException, ParseException {
		this.parameterMap = parameterMap;
		loadFromParams(prefix);
	}	
	
	protected String getMapValue(String key) {	
		if (parameterMap.containsKey(key)) {
			return parameterMap.get(key)[0].trim();			
		}
		return null;
	}
	
	public void loadFromParams(String prefix) throws IllegalArgumentException, IllegalAccessException, ParseException {	
		for (Field field : getFields()) {
//			log.info(field.getName());
			field.setAccessible(true);
			if (parameterMap.containsKey(prefix.concat(field.getName()))) {
				String value = parameterMap.get(prefix.concat(field.getName()))[0].trim();
				log.info(field.getName()+"="+value);				
				if (StringUtils.isNotBlank(value)) {
					
					if (field.getType().equals(String.class)) {
						field.set(this, value);
					} else if (field.getType().equals(Text.class)) {
						field.set(this, Utils.textValue(value));
					}  else if (field.getType().equals(Date.class)) {
						field.set(this, Utils.parseDate(value));
					} else if (field.getType().equals(Integer.class)) {
						field.set(this, Utils.formatInt(value));
					} else if (field.getType().equals(Double.class)) {
						field.set(this, Utils.formatDouble(value));
					} else if (field.getType().equals(Key.class)) {
						field.set(this, KeyFactory.stringToKey(value));
					} else if (field.getType().equals(Link.class)) {
						field.set(this, Utils.linkValue(value));
					} else if (field.getType().equals(Rating.class)) {
						field.set(this, Utils.ratingValue(value));
					} 
					
				}
			}
		}
	}
/*	
	public Map<String, Object> getAttributeMap(){
		Map<String, Object> attMap = new HashMap<String, Object>();
		
		for (Field field : getFields()) {
			field.setAccessible(true);
			try {
				attMap.put(field.getName(), field.get(this));
			} catch (Exception e) {
				log.log(Level.SEVERE, "Failed to Get Field Value", e);
			}
		}
		
		return attMap;
	}*/

	public Field[] getFields(){
		return getClass().getDeclaredFields();
	}
	
	public static final PersistenceManager entityManager() {
		return PMF.get().getPersistenceManager();
	}
	
	public void delete() {
		PersistenceManager em = entityManager();
		Transaction tx = em.currentTransaction();
		try {
			tx.begin();
			em.deletePersistent(em.getObjectById(getClass(), getKey()));
			tx.commit();
		} finally {
			if (tx.isActive()) {
				tx.rollback();
			}
			em.close();
		}
	}

	public void save() {
		PersistenceManager em = entityManager();
		Transaction tx = em.currentTransaction();
		try {
			tx.begin();
			em.makePersistent(this);
			tx.commit();
		} finally {
			if (tx.isActive()) {
				tx.rollback();
			}
			em.close();
		}
	}
	
	public static long rowCount(Class<?> clazz, String where, Object[] whereValues){
		PersistenceManager em = entityManager();
		StringBuilder sb = new StringBuilder("select count(o) from ");
		sb.append(clazz.getName());
		sb.append(Constants.EMPTY_STRING);
		try {
			
			if(StringUtils.isNotBlank(where)){
				sb.append(where);			
			}
			
			Query q = em.newQuery(sb.toString());
			long value = ((Number) q.executeWithArray(whereValues)).longValue() ;
			
			return value;
		}catch(Exception e){
			log.log(Level.SEVERE, "Failled to Retrieve the Row Count", e);
			return 0;
		} 
		finally {
			em.close();
		}
	}
	

	public static <T> T selectById(Class<T> clazz, Object id) {
		if (id == null) {
			return null;
		}
		PersistenceManager em = entityManager();
		try {
			return em.getObjectById(clazz, id);
		} finally {
			em.close();
		}
	}
	
	public static <T> List<T> selectAll(Class<T> clazz) {		
		return select(clazz, -1, -1);
	}
	
	public static <T> List<T> selectAll(Class<T> clazz, String orderBy) {
		return select(clazz, -1, -1, orderBy, null);
	}

	public static <T> List<T> select(Class<T> clazz, int firstResult, int maxResults) {
		return select(clazz, firstResult, maxResults, null, null);
	}
	
	public static <T> List<T> select(Class<T> clazz, String where, Object[] whereValues) {
		return select(clazz, -1, -1, where, whereValues);
	}
	
	
	public static <T> List<T> select(Class<T> clazz, String where, Object[] whereValues,  String[] declaredVar) {
		return select(clazz, -1, -1, where, whereValues, declaredVar);
	}
	
	public static <T> T selectAsSingleResult(Class<T> clazz,String where, Object[] whereValues) {
		List<T> lst = (select(clazz,-1,1,where,whereValues));
		return lst.size() > 0 ?  lst.get(0) : null; 
	}
	
	public static <T> List<T> select(Class<T> clazz,int firstResult, int maxResults, String where, Object[] whereValues) {
		return select( clazz, firstResult, maxResults, where, whereValues, null) ;
	}
	
	public static <T> List<T> select(Class<T> clazz,int firstResult, int maxResults, String where, Object[] whereValues, String[] declaredVar) {
		PersistenceManager em = entityManager();
		try {
			
			whereValues = (whereValues!=null) ? whereValues : new Object[]{} ; 
				
			StringBuffer buf = new StringBuffer("select ");
			buf.append(" from ").append(clazz.getName());
			buf.append(Constants.EMPTY_STRING);
			boolean filterEnabled = StringUtils.isNotBlank(where) && (where.startsWith("where") || where.startsWith("WHERE") );
			if (filterEnabled) {
				buf.append(where);
			}
			
			if(StringUtils.isNotBlank(where) && (where.startsWith("order by") || where.startsWith("ORDER BY") )){
				buf.append(where);
			}
			
			log.info("Sql " + buf.toString());
			/*log.info("Where Columns" + xs.toXML(whereColumns));
			log.info("Where Values" + xs.toXML(whereValues));*/
			
			Query q = em.newQuery(buf.toString());
			
			if(declaredVar != null){
				for(String dvar : declaredVar){
					q.declareVariables(dvar);
				}
			}
			
			if ( (firstResult != -1) && (maxResults != -1)){
				q.setRange(firstResult, maxResults);
			}

			List<T> lst = (List<T>) q.executeWithArray(whereValues);
			
			// force to materialize
			lst.size();
			return lst;
		} finally {
			em.close();
		}
	}
	
	protected static String newWarning(String field) {
		return "<div><font class='reviewalertmessages'>"+field+"</font></div>";
	}
	
	public String fireEvents(String userAction, HttpServletRequest request) throws Exception{
		if (userAction.equals(Constants.Action.NEW.toString()) || userAction.equals(Constants.Action.EDIT.toString())) {
			this.save();
			performPostPersistEvents(userAction, request);
			return newWarning(Constants.Transaction.Success.getMsg());
		}
		log.info( Constants.UNKNOWN_REQUEST +userAction);
		return Constants.UNKNOWN_REQUEST;
	}
	
	protected void performPostPersistEvents(String userAction,
			HttpServletRequest request) throws Exception {
	}


	public static boolean executeBatch(List<BusinessObject> batchList) {
		PersistenceManager em = entityManager();
		Transaction tx = em.currentTransaction();
		Key prevKey = null;
		try {
			tx.begin();
			
			for(BusinessObject bo : batchList){
				if(bo.getKey() == null){
					prevKey = (prevKey!= null) ? prevKey = KeyFactory.createKey(prevKey,bo.getClass().getSimpleName(), bo.getNamer()) :
						KeyFactory.createKey(bo.getClass().getSimpleName(), bo.getNamer());
					bo.setKey(prevKey);
				}
				em.makePersistent(bo);
			}
			
			tx.commit();
			return true;
		}catch(Exception e){			
			log.log(Level.SEVERE, "Failed to Execute Batch Update", e);
			return false;
		}finally {
			if (tx.isActive()) {
				tx.rollback();
			}
			em.close();			
		}
	}
	
	public String validate() {
		return null;
	}

	public void setLastModifyUser(String lastModifyUser) {
		
	}

	public void setCreatedByUser(String createdByUser) {
		
	}

	public String getNamer() {
		return null;
	}

}

